/*
 * Moseycode
 * Copyright (C) 2008  Tom Gibara
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package com.tomgibara.mosey.coding;

/**
 * Generates cyclic redundancy checksum from a stream of bits.
 * 
 * @author Tom Gibara
 */

public final class CRC16 {

	// statics

	/**
	 * Default polynomial for performing the CRC check.
	 * This is CRC-16-ANSI (normal).
	 */
	
	public static final short DEFAULT_POLY = (short) 0x8005;
	
	// fields
	
	/**
	 * The polynomial used to calculate the checksum.
	 */
	
	private final short mPolynomial;
	
	/**
	 * The checksum generated from the supplied bits.
	 */
	
	private short mChecksum;

	// constructors
	
	/**
	 * Create a new checksum calculator which will use the {@link DEFAULT_POLY}.
	 */
	
	public CRC16() {
		this(DEFAULT_POLY);
	}
	
	/**
	 * Create a new checksum calculator which will use the supplied polynomial.
	 * 
	 * @param polynomial the generating polynomial for the checksum in normal form.
	 */
	
	public CRC16(final short polynomial) {
		mPolynomial = polynomial;
		reset();
	}

	// accessors
	
	/**
	 * The (normal form) polynomial used to generate the checksum.
	 * 
	 * @return the polynomial used to generate the checksum.
	 */

	public short getPolynomial() {
		return mPolynomial;
	}
	
	// methods
	
	/**
	 * Adds a specified number of bits from the supplied int to the stream of
	 * bits from which the checksum is being generated. The least significant
	 * bits of the value are added, with the most significant bits of those
	 * added first.
	 * 
	 * @param value an int containing bits to be checksummed
	 * @param length the number of bits to be checksummed
	 */

	public void addBits(final int value, final int length) {
		int mask = 1 << (length - 1);
		do {
			if (((mChecksum & 0x8000) == 0) ^ ((value & mask) == 0)) {
				mChecksum <<= 1;
				mChecksum ^= mPolynomial;
			} else {
				mChecksum <<= 1;
			}
		} while ((mask >>>= 1) != 0);
	}

	/**
	 * Adds all of the bits in the supplied int to the stream of bits from
	 * which the checksum is being generated.
	 * 
	 * @param value contains bits to be checksummed
	 */

	public void addInt(final int value) {
		addBits(value, 32);
	}

	/**
	 * Adds all of the bits in the supplied short to the stream of bits from
	 * which the checksum is being generated.
	 * 
	 * @param value contains bits to be checksummed
	 */

	public void addShort(final short value) {
		addBits(value, 16);
	}
	
	/**
	 * Adds all of the bits in the supplied byte to the stream of bits from
	 * which the checksum is being generated.
	 * 
	 * @param value contains bits to be checksummed
	 */

	public void addByte(final byte value) {
		addBits(value, 8);
	}
	
	/**
	 * The checksum computed from the bits added. This method may be called at
	 * any time. In particular, it may be called prior to adding more bits, in
	 * which case the checksum of the incomplete bitstream is returned.
	 * 
	 * @return the computed checksum (erroneous)
	 */

	public short checksum() {
		return mChecksum;
	}
	
	/**
	 * Resets the checksum calculator to its initial state. This allows the
	 * checksum calculator to be reused to calculate the checksum for a new
	 * bitstream.
	 */

	public void reset() {
		mChecksum = (short) 0xFFFF;
	}
}